//============================================================================
// Name        : CMPT300Project2.cpp
// Author      : james
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <stdio.h>
#include <iostream>
#include <pthread.h>
#include <list>
#include <pthread.h>
#include "LongTermScheduler.h"
#include <stdlib.h>
#include "CPU.h"
#include "BlockedQ.h"
#include "RuntimeParameters.h"
#include <unistd.h>

using namespace std;

void* ltsThreadCreate(void* threadId);
void* cpuThreadCreate(void* cpu);
void* ioThreadCreate(void* threadId);
void* resetThreadCreate(void* threadId);
bool simulationFinished();
void displayProcessSummary();
void displayCpuSummary(CPU* cpu[]);

int main() {
	// Create processes and put them into a queue
	LongTermScheduler* lts = LongTermScheduler::getInstance();

	// Make sure that the instances are created so that the threads will get the same instance
	BlockedQ::getInstance();
	PriorityQ::getInstance();
	FinishedQ::getInstance();

	// Fill the lts with new process
	for(int i = 0; i < NUMBER_OF_PROCESSES; i++) {
		cout << "Adding new process to Long Term Scheduler \n";
		lts->addNewProcess(new Process(i));
	}


	// Start the simulation
	// Create the threads

	pthread_t ltsThread;
	pthread_t ioThread;
	pthread_t cpuThread[NUMBER_OF_CPUS];
	pthread_t mlfqResetThread;
	CPU* cpu[NUMBER_OF_CPUS];

	int r = pthread_create(&ltsThread, NULL, ltsThreadCreate, (void *) 0);
	// If an error occurred when creating a thread, exit the program.
	if(r) {
		cout << "Error occurred: exiting";
		exit(-1);
	}

	for(int i = 0; i < NUMBER_OF_CPUS; i++) {

		cpu[i] = new CPU(DEFAULT_TIME_QUANTUM, i);

		r = pthread_create(&(cpuThread[i]), NULL, cpuThreadCreate, ((void*) cpu[i]) );
		// If an error occurred when creating a thread, exit the program.
		if(r) {
			cout << "Error occurred: exiting";
			exit(-1);
		}
	}

	r = pthread_create(&(ioThread), NULL, ioThreadCreate, (void *) 1);
	// If an error occurred when creating a thread, exit the program.
	if(r) {
		cout << "Error occurred: exiting";
		exit(-1);
	}


	r = pthread_create(&mlfqResetThread, NULL, resetThreadCreate, (void *) 2);
	// If an error occurred when creating a thread, exit the program.
	if(r) {
		cout << "Error occurred: exiting";
		exit(-1);
	}


	while (true) {
		if ( simulationFinished() ) {
			displayProcessSummary();
			displayCpuSummary(cpu);
			break;
		}
	}

	pthread_cancel(ltsThread);
	pthread_cancel(ioThread);
	pthread_cancel(mlfqResetThread);
	for(int i = 0; i < NUMBER_OF_CPUS; i++) {
		pthread_cancel(cpuThread[i]);
	}
	pthread_exit(NULL);
}

void* ltsThreadCreate(void* threadId) {
	printf("Starting the LTS\n");
	LongTermScheduler::getInstance()->run();
	pthread_exit(NULL);
	return NULL;
}

void* cpuThreadCreate(void* cpu) {
	printf("Starting CPU%d thread\n", ((CPU*)cpu)->getCpuID());
	((CPU*)cpu)->run();
	pthread_exit(NULL);
	return NULL;
}

// Simulate IO operations
void* ioThreadCreate(void* threadId) {
	printf("Starting the IO thread\n");
	BlockedQ* bq = BlockedQ::getInstance();
	while(true) {
		bq->tick();
	}

	pthread_exit(NULL);
	return NULL;
}

void* resetThreadCreate(void* threadId) {
	printf("Starting the reset thread\n");
	BlockedQ* bq = BlockedQ::getInstance();
	PriorityQ* pq = PriorityQ::getInstance();
	while(true) {
		usleep(MICROSECONDS_STS_RESET_TIME);
		bq->resetPriorityRuntime();
		pq->resetPriorityRuntime();
	}

	pthread_exit(NULL);
	return NULL;
}

bool simulationFinished() {
	FinishedQ* finishedQ = FinishedQ::getInstance();
	return (finishedQ->getFinishedQSize() == NUMBER_OF_PROCESSES) ;
}
void displayProcessSummary() {
	FinishedQ* finishedQ = FinishedQ::getInstance();
	finishedQ->displaySummary();
}

void displayCpuSummary(CPU* cpu[]) {
	printf("Nr of cpu: %d\n", NUMBER_OF_CPUS);
	for(int i = 0; i < NUMBER_OF_CPUS; i++) {
		cpu[i]->calculateTotalCpuLifetime();
		cpu[i]->displayCpuSummary();
	}
}
